package cast2

import (
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"gitee.com/u-language/u-language/ucom/ast2"
	"gitee.com/u-language/u-language/ucom/data"
)

type Package struct {
	initstr     string
	packageName string
	headerFile  data.Slice[ast2.CDecl]
	tocs        data.Slice[UtoC]
	varInit     []*ast2.VarNode
	importAll   []*Package
	//并发
	thread       bool
	haveInitFunc bool
}

func NewPackage(Thread bool, p *ast2.Package, firstcall bool) *Package {
	ret := &Package{thread: Thread, headerFile: data.Slice[ast2.CDecl]{Thread: Thread}, tocs: data.Slice[UtoC]{Thread: Thread}, packageName: p.PackageName}
	ret.addUastSlice(p)
	if firstcall {
		for i := range p.ImportPackage {
			ret.importAll = append(ret.importAll, NewPackage(Thread, p.ImportPackage[i], false))
		}
	}
	return ret
}

func (p *Package) addUastSlice(astp *ast2.Package) {
	if len(astp.Trees.Data) == 0 { //如果包里面没有抽象语法树
		return
	}
	p.packageName, p.headerFile.Data = astp.PackageName, astp.CHeaderFile.Data
	p.haveInitFunc = astp.IsInitFunc
	for i := range astp.Trees.Data { //将所有U语言的抽象语法树转换为C语言的抽象语法树
		toc := newUtoC(astp.Trees.Data[i])
		p.tocs.Add(toc)
	}

	//排序使输出可复现
	sort.Slice(p.tocs.Data, func(i, j int) bool {
		return p.tocs.Data[i].FileName < p.tocs.Data[j].FileName
	})

	//建立按行升序的全局变量初始化顺序
	p.varInit = astp.VarInitTable.InitOrder()
}

// OupputC 输出C文件到目录
func (p *Package) OupputC(dir string) (string, error) {
	var err error
	cname := filepath.Join(dir, p.packageName) + ".c"
	fd, err := os.Create(cname)
	if err != nil {
		return "", err
	}
	p.Oupput(fd, p.GenerateheaderFile())
	fd.Close()
	return cname, nil
}

func (p *Package) Oupput(w io.StringWriter, header string) {
	var buf *strings.Builder
	// if b, ok := w.(*strings.Builder); ok {
	// 	buf = b
	// } else {
	buf = new(strings.Builder)
	//}
	buf.WriteString(includeStr)
	for i := range p.importAll {
		p.importAll[i].oupput(buf, p.importAll[i].GenerateheaderFile())
	}
	p.oupput(buf, header)
	w.WriteString(buf.String())
}

func (p *Package) oupput(buf *strings.Builder, header string) {
	//写入头文件
	buf.WriteString(header)
	buf.WriteString("\n")
	for _, v := range p.tocs.Data { //生成自己的C代码
		generateCFile(v.Nodes, buf)
	}
	buf.WriteString(p.initstr)
}

func (p *Package) GenerateheaderFile() string {
	var buf strings.Builder
	buf.WriteString("#ifndef ")
	buf.WriteString("_")
	buf.WriteString(p.packageName)
	buf.WriteString("_H__ \n")
	buf.WriteString("#define ")
	buf.WriteString("_")
	buf.WriteString(p.packageName)
	buf.WriteString("_H__ \n")
	// if p.WithAutoFree() {
	// 	buf.WriteString("#include \"mempool.h\"\n")
	// }
	//生成全局声明
	sort.Slice(p.headerFile.Data, func(i int, j int) bool { //排序是为了保证可复现的构建，和类型声明在最前面
		return nodeCmp(i, j, p.headerFile.Data)
	})
	buf.WriteString("//--- 全局声明 ---\n")
	for _, v := range p.headerFile.Data {
		v.CDecl(&buf)
		buf.WriteString("\n")
	}
	buf.WriteString("//--- init声明 ---\n")
	//生成init声明
	p.initstr = generateInit(p.packageName, p.haveInitFunc, p.varInit, p.importAll, &buf)
	buf.WriteString("#endif\n")
	return buf.String()
}

func (p *Package) String() string {
	var buf strings.Builder
	p.Oupput(&buf, p.GenerateheaderFile())
	return buf.String()
}
